package oceanlife.evostrat;

import java.util.ArrayList;

import oceanlife.OLSet;
import oceanlife.RandomGen;
import oceanlife.log.Logger;
import oceanlife.pathfinding.Pillar;
import oceanlife.pathfinding.Pnt;
import oceanlife.pathfinding.SimpleObstacle;

public class GeneCrossing implements Gene {

	private double[] genes;
	private double[] stepSizes;
	private double globalLearningRate;
	private double learningRate;
	public static double xSize = 225.0;
	public static double ySize = 225.0;
	public static double corLength = 40.0;
	public static double radius = 10.0;
	private static double crossingSubsectionSize = (xSize - 2 * corLength);
	private static double range = crossingSubsectionSize - 2 * radius;

	
	public double getMaxValue(int index){
		return range;
	}
	public double getMinValue(int index){
		return 0.0;
	}
	public boolean isValid(){
		ArrayList<SimpleObstacle> obs = getSimpleObstacles();
		for(int i = 0; i < obs.size(); i++){
			for(int j = 0; j < obs.size(); j++){
				if(j == i){
					continue;
				} else {
					if(obs.get(i).intersectsWithObstacle(obs.get(j))){
						//System.out.println("overlapping child");
						return false;
					}
				}
			}
		}
		return true;
	}
	@Override
	public void setGenes(double[] genes) {
		this.genes = genes;
	}
	@Override
	public void setStepSizes(double[] stepSizes) {
		this.stepSizes = stepSizes;
	}
	@Override
	public void setGlobalLearningRate(double globalLearningRate) {
		this.globalLearningRate = globalLearningRate;
	}
	@Override
	public void setLearningRate(double learningRate) {
		this.learningRate = learningRate;
	}
	@Override
	public double[] getGenes() {
		return genes;
	}
	@Override
	public double[] getStepSizes() {
		return stepSizes;
	}
	@Override
	public double getGlobalLearningRate() {
		return globalLearningRate;
	}
	@Override
	public double getLearningRate() {
		return learningRate;
	}
	@Override
	public Gene makeChild(RandomGen rand) {
		boolean validChild = false;
		while(!validChild){
			GeneCrossing child = new GeneCrossing();
			child.genes = new double[this.genes.length];
			child.stepSizes = new double[this.genes.length];
			child.globalLearningRate = this.globalLearningRate;
			double globalTau = this.globalLearningRate*rand.getGaussian();
			child.stepSizes[0] = this.stepSizes[0] * Math.exp(this.learningRate*rand.getGaussian()+globalTau);//hack for single stepsize
			for(int i = 0; i < this.genes.length; i++){
				child.genes[i] = this.genes[i] + child.stepSizes[0] * rand.getGaussian();
				while(child.genes[i] > child.getMaxValue(i) || child.genes[i] < child.getMinValue(i)){
					if(child.genes[i] > child.getMaxValue(i)){
						child.genes[i] = child.getMaxValue(i) - (child.genes[i] - child.getMaxValue(i));
					} else {
						child.genes[i] = child.getMinValue(i) - (child.genes[i] - child.getMinValue(i));
					}
				}
				child.learningRate = this.learningRate;
			}
			validChild = child.isValid();
			if(validChild){
				return child;
			}
		}
		System.err.println("couldn't make a propper child");
		return null;
	}
	@Override
	public ArrayList<SimpleObstacle> getSimpleObstacles() {
		ArrayList<SimpleObstacle> simpleObs = new ArrayList<SimpleObstacle>();

		
		
		for(int i = 0; i < 4; i++){
			simpleObs.add(makeCrossingObs(this.genes[i*2],this.genes[i*2+1],corLength,corLength));
		}

		simpleObs.add(new SimpleObstacle(new Pnt[]{new Pnt(corLength,corLength),new Pnt(corLength,-1000.0),new Pnt(-1000.0,-1000.0),new Pnt(-1000.0,corLength)}));
		simpleObs.add(new SimpleObstacle(new Pnt[]{new Pnt(xSize-corLength,corLength),new Pnt(1000.0,corLength),new Pnt(1000.0,-1000.0),new Pnt(xSize-corLength,-1000.0)}));
		simpleObs.add(new SimpleObstacle(new Pnt[]{new Pnt(xSize-corLength,ySize-corLength),new Pnt(xSize-corLength,1000.0),new Pnt(1000.0,1000.0),new Pnt(1000.0,ySize-corLength)}));
		simpleObs.add(new SimpleObstacle(new Pnt[]{new Pnt(corLength,ySize-corLength),new Pnt(-1000.0,ySize-corLength),new Pnt(-1000.0,1000.0),new Pnt(corLength,1000.0)}));

		return simpleObs;
	}

	private SimpleObstacle makeCrossingObs(double geneX, double geneY, double offsetX, double offsetY) {
		return new SimpleObstacle(new Pnt[]{
			new Pnt(offsetX+geneX,offsetY+geneY),
			new Pnt(offsetX+geneX,offsetY+geneY+2*radius),
			new Pnt(offsetX+geneX+2*radius,offsetY+geneY+2*radius),
			new Pnt(offsetX+geneX+2*radius,offsetY+geneY)
		});
	}
	public static Gene getInitChild() {
		RandomGen rand = RandomGen.getNextRandomGen();
		int obstacles = 4;
    	double[] genes = new double[obstacles*2];
    	double[] stepSizes = new double[obstacles*2];
    	double learningRate = 1.0/Math.sqrt(2*genes.length);
    	double globalLearningRate = 1.0/Math.sqrt(2*Math.sqrt(genes.length));
    	for(int i = 0; i < obstacles; i++){
    		genes[i*2] = rand.getRand(0, range);	
    		genes[i*2+1] = rand.getRand(0, range);
    		stepSizes[i*2] = range/Math.sqrt(10);
    		stepSizes[i*2+1] = range/Math.sqrt(10);
    	}
		GeneCrossing gene = new GeneCrossing();
    	gene.setGenes(genes);
    	gene.setStepSizes(stepSizes);
    	gene.setLearningRate(learningRate);
    	gene.setGlobalLearningRate(globalLearningRate);
		return gene;
	}
	@Override
	public String printGenes() {
		String returnString = "";
		for(int i = 0; i < genes.length; i+=2){
			returnString += ("Gene: X:"+genes[i]+" Y:"+genes[i+1]) + '\n';
		}
		return returnString;
	}
	
	@Override
	public String toString(){
		String ret = "";
		for(int i = 0; i < genes.length; i+=2){
			ret += " "+(i/2+1)+" X:"+genes[i]+" Y:"+genes[i+1];
		}
		return ret;
	}
	
}
